﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml.Xsl;
using System.Xml;
using System.Diagnostics;
using System.Xml.Schema;

namespace XmlTools
{
    public partial class MainFrm : Form
    {
        protected List<XmlSchemaException> m_ValidationErrors = null;
        protected List<string> m_ValidationWarnings = null;
        public struct XsltParam
        {
            public string Name { get; set; }
            public string NamespaceUri { get; set; }
            public object Value { get; set; }
        }


        public MainFrm()
        {
            InitializeComponent();
        }

        private void MainFrm_FormClosed(object sender, FormClosedEventArgs e)
        {
            
        }

        public string XsltInputPath
        {
            get { return Properties.Settings.Default.XsltInputPath; }
            set
            {
                if (value != Properties.Settings.Default.XsltInputPath)
                {
                    Properties.Settings.Default.XsltInputPath = value;
                    Properties.Settings.Default.Save();
                    OnXsltInputPathChanged(EventArgs.Empty);
                } 
            }
        }
        public event EventHandler XsltInputPathChanged;
        protected virtual void OnXsltInputPathChanged(EventArgs e)
        {
            OnXsltOutputPathChanged(e);
            if (XsltInputPathChanged != null)
            {
                XsltInputPathChanged(this, e);
            }
        }

        public string XsltTransformPath
        {
            get { return Properties.Settings.Default.XsltTransformPath; }
            set
            {
                if (value != Properties.Settings.Default.XsltTransformPath)
                {
                    Properties.Settings.Default.XsltTransformPath = value;
                    Properties.Settings.Default.Save();
                    OnXsltTransformPathChanged(EventArgs.Empty);
                }
            }
        }
        public event EventHandler XsltTransformPathChanged;
        protected virtual void OnXsltTransformPathChanged(EventArgs e)
        {
            OnXsltOutputPathChanged(e);
            if (XsltTransformPathChanged != null)
            {
                XsltTransformPathChanged(this, e);
            }
        }

        public string XsltOutputFile
        {
            get { return Properties.Settings.Default.XsltOutputFile; }
            set
            {
                if (value != Properties.Settings.Default.XsltOutputFile)
                {
                    Properties.Settings.Default.XsltOutputFile = value;
                    Properties.Settings.Default.Save();
                    OnXsltOutputFileChanged(EventArgs.Empty);
                }
            }
        }
        public event EventHandler XsltOutputFileChanged;
        protected virtual void OnXsltOutputFileChanged(EventArgs e)
        {
            OnXsltOutputPathChanged(e);
            if (XsltOutputFileChanged != null)
            {
                XsltOutputFileChanged(this, e);
            }
        }


        public string XsltOutputPath
        {
            get 
            {
                try
                {
                    
                    if (!File.Exists(Properties.Settings.Default.XsltInputPath))
                        throw new FileNotFoundException("Can't find input file");
                    if (!File.Exists(Properties.Settings.Default.XsltTransformPath))
                        throw new FileNotFoundException("Can't find transform file");
                    string directory = Path.GetDirectoryName(Properties.Settings.Default.XsltInputPath);
                    string path = Path.Combine(directory, Properties.Settings.Default.XsltOutputFile);
                    buttonXsltStart.Enabled = true;
                    linkLabelXsltOutputPath.Enabled = File.Exists(path);
                    textBoxXsltResults.Text = "";
                    textBoxXsltResults.ForeColor = Color.Black;
                    return path;
                }
                catch (Exception x)
                {
                    buttonXsltStart.Enabled = linkLabelXsltOutputPath.Enabled = false;
                    textBoxXsltResults.Text = x.Message;
                    textBoxXsltResults.ForeColor = Color.Red;
                    return "Error";
                }
            }
        }
        public event EventHandler XsltOutputPathChanged;
        protected virtual void OnXsltOutputPathChanged(EventArgs e)
        {
            if (XsltOutputPathChanged != null)
            {
                XsltOutputPathChanged(this, e);
            }
        }

        private void MainFrm_Load(object sender, EventArgs e)
        {
            linkLabelXsltOutputPath.DataBindings.Add("Text", this, "XsltOutputPath");
            textBoxXsltInputFile.DataBindings.Add("Text", this, "XsltInputPath");
            textBoxXsltTransformFile.DataBindings.Add("Text", this, "XsltTransformPath");
            textBoxXsltOutputName.DataBindings.Add("Text", this, "XsltOutputFile");
        }

        private void buttonXsltStart_Click(object sender, EventArgs e)
        {
            if (File.Exists(XsltOutputPath) && 
                DialogResult.Yes != MessageBox.Show("Output file already exists. Overwrite?", "Overwrite?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2))
                return;
            
            Transform(XsltTransformPath, XsltInputPath, XsltOutputPath, null);
            string s = XsltOutputPath;
            textBoxXsltResults.Text += string.Format("Finished. Click link above to view results.");
        }

        public void Transform(string xsltPath, string inputPath, string outputPath, XsltParam[] parameters)
        {
            this.Enabled = false;
            XsltSettings settings = new XsltSettings(true, true);
            XmlUrlResolver resolver = new XmlUrlResolver();
            XslCompiledTransform t = new XslCompiledTransform();
            t.Load(xsltPath, settings, resolver);
            XmlReader r = XmlReader.Create(inputPath);
            XmlWriterSettings writerSettings = new XmlWriterSettings();
            writerSettings.Indent = true;
            XmlWriter w = XmlWriter.Create(outputPath, writerSettings);
            XsltArgumentList al = new XsltArgumentList();
            if (parameters != null)
            {
                foreach (XsltParam p in parameters)
                {
                    al.AddParam(p.Name, p.NamespaceUri, p.Value);
                }
            }
            List<string> _messageList = new List<string>();
            al.XsltMessageEncountered += new XsltMessageEncounteredEventHandler(delegate(object sender, XsltMessageEncounteredEventArgs e)
            {
                textBoxXsltResults.Text += e.Message + Environment.NewLine;
            });
            try
            {
                t.Transform(r, al, w);
            }
            catch (Exception x)
            {
                throw x;
            }
            finally
            {
                r.Close();
                w.Close();
                this.Enabled = true;
            }
        }

        private void linkLabelXsltOutputPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            this.Enabled = false;
            try
            {
                Process.Start(linkLabelXsltOutputPath.Text);
            }
            finally
            {
                this.Enabled = true;
            }
        }

        private void buttonValidation_Click(object sender, EventArgs e)
        {
            string sXmlPath = textBoxValidationInputFile.Text;
            string sXsdPath = textBoxValidationSchemaFile.Text;
            XmlReader rxsd = XmlReader.Create(sXsdPath);
            
            XmlSchemaSet s = new XmlSchemaSet();
            s.Add(XmlSchema.Read(rxsd, new ValidationEventHandler(OnSchemaValidationEvent)));
            rxsd.Close();
            XmlDocument d = new XmlDocument();
            d.Schemas = s;
            d.Load(sXmlPath);
            m_ValidationErrors = new List<XmlSchemaException>();
            m_ValidationWarnings = new List<string>();
            d.Validate(new ValidationEventHandler(OnDocValidationEvent));

            StringBuilder sb = new StringBuilder();
            foreach (XmlSchemaValidationException x in m_ValidationErrors)
            {
                sb.AppendLine();
                sb.AppendFormat("{0}", x.Message);
                sb.AppendLine();
                sb.AppendFormat("\tOn Line{0}, position {1}", x.LineNumber, x.LinePosition);
                sb.AppendLine();
                if (x.SourceObject != null)
                {
                    if (x.SourceObject is XmlAttribute)
                    {
                        XmlAttribute a = x.SourceObject as XmlAttribute;
                        sb.AppendLine(String.Format("\t\tAttribute: {0}=\"{1}\"", a.Name, a.Value));
                    }
                    else if (x.SourceObject is XmlElement)
                    {
                        XmlElement elem = x.SourceObject as XmlElement;
                        sb.Append(String.Format("\t\tElement: <{0} ", elem.Name));
                        foreach (XmlAttribute a in elem.Attributes)
                        {
                            sb.AppendLine(String.Format("\t\t\t\t{0}=\"{1}\" ", a.Name, a.Value));
                        }
                        sb.AppendLine(String.Format(">"));
                    }
                    else if (x.SourceObject is XmlText)
                    {
                        XmlText t = x.SourceObject as XmlText;
                        sb.AppendLine(String.Format("\t\tText: \"{0}\"", t.Data.Replace(Environment.NewLine, @"[NEWLINE]")));
                    }
                    else
                    {
                        sb.AppendLine(String.Format("\t\t{0}", x.SourceObject.ToString()));
                    }

                }
            }
            foreach (string sValidation in m_ValidationWarnings)
            {
                sb.AppendLine(String.Format("Warning:{0}", sValidation));
            }
            if (m_ValidationErrors.Count > 0)
            {
                m_ValidationErrors = null;
                m_ValidationWarnings = null;
                MessageBox.Show("Document Validation Failed");
            }
            textBoxValidationErrors.Text = sb.ToString();
            rxsd.Close();
        }

        private void OnSchemaValidationEvent(Object sender, ValidationEventArgs e)
        {
            if (e.Severity == XmlSeverityType.Error)
                m_ValidationErrors.Add(e.Exception);
            else
                m_ValidationWarnings.Add(string.Format("Warning: {0}", e.Message));
        }
        private void OnDocValidationEvent(Object sender, ValidationEventArgs e)
        {
            if (e.Severity == XmlSeverityType.Error)
                m_ValidationErrors.Add(e.Exception);
            else
                m_ValidationWarnings.Add(string.Format("Warning: {0}", e.Message));
        }

        private void btnInputXmlFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Xml File|*.xml";
            dlg.ShowDialog();
            textBoxValidationInputFile.Text = dlg.FileName;
        }

        private void btnSchemaFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Xsd Schema File|*.xsd";
            dlg.ShowDialog();
            textBoxValidationSchemaFile.Text = dlg.FileName;
        }

        private void textBoxValidationInputFile_TextChanged(object sender, EventArgs e)
        {
            EnableValidateBtn(sender, e);
        }

        private void EnableValidateBtn(object sender, EventArgs e)
        {
            if (File.Exists(textBoxValidationSchemaFile.Text) &&
                File.Exists(textBoxValidationInputFile.Text))
            {
                buttonValidation.Enabled = true;
            }
            else
            {
                buttonValidation.Enabled = false;
            }
        }

        private void textBoxValidationSchemaFile_TextChanged(object sender, EventArgs e)
        {
            EnableValidateBtn(sender, e);
        }
    }
}
